package jwt

import (
	"errors"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"time"
	"wx_build/core/logs"
)

// 用户信息类，作为生成token的参数
type UserClaims struct {
	UserId   string `json:"user_id"`
	Username string `json:"username"` //用户名
	Nickname string `json:"nickname"` //昵称
	//jwt-go提供的标准claim
	jwt.StandardClaims
}

var (
	//自定义的token秘钥
	secret = []byte("DGV7eynpoHaYs8N16tJgjlSKxhZiwXbd")
	//该路由下不校验token
	//noVerify = []interface{}{"/login", "/ping"}
	//token有效时间（纳秒）
	effectTime = 2 * time.Hour
)

// 生成token
func GenerateToken(claims *UserClaims) string {
	//设置token有效期，也可不设置有效期，采用redis的方式
	//   1)将token存储在redis中，设置过期时间，token如没过期，则自动刷新redis过期时间，
	//   2)通过这种方式，可以很方便的为token续期，而且也可以实现长时间不登录的话，强制登录
	//本例只是简单采用 设置token有效期的方式，只是提供了刷新token的方法，并没有做续期处理的逻辑
	claims.ExpiresAt = time.Now().Add(effectTime).Unix()
	//生成token
	sign, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(secret)
	if err != nil {
		//这里因为项目接入了统一异常处理，所以使用panic并不会使程序终止，如不接入，可使用原始方式处理错误
		//接入统一异常可参考 https://blog.csdn.net/u014155085/article/details/106733391
		panic(err)
	}
	return sign
}

// 验证token
func JwtVerify(c *gin.Context, tokenName string) (*UserClaims, error) {
	//过滤是否验证token
	//token, err := c.Cookie(tokenName)
	//if err != nil {
	//	logs.Err("token为空，登录失败")
	//	return nil, errors.New("登录验证失败")
	//}
	token := ""
	if tokenName == "rise_token" {
		token = c.Request.Header.Get("rise_token")
	} else {
		tokens, err := c.Cookie(tokenName)
		token = tokens
		if err != nil {
			logs.Err("token为空，登录失败")
			return nil, errors.New("登录验证失败")
		}
	}
	if token == "" {
		logs.Err("token为空，登录失败")
		return nil, errors.New("登录验证失败")
	}
	userClaims, err := parseToken(token)
	if err != nil {
		return nil, errors.New("token过期")
	}
	return userClaims, nil
}

// 解析Token
func parseToken(tokenString string) (*UserClaims, error) {
	//解析token
	token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
		return secret, nil
	})
	if err != nil {
		return nil, errors.New("token已过期")
	}
	claims, ok := token.Claims.(*UserClaims)
	if !ok {
		return nil, errors.New("token已过期")
	}
	return claims, nil
}

// 更新token
func Refresh(tokenString string) string {
	jwt.TimeFunc = func() time.Time {
		return time.Unix(0, 0)
	}
	token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
		return secret, nil
	})
	if err != nil {
		panic(err)
	}
	claims, ok := token.Claims.(*UserClaims)
	if !ok {
		panic("token is valid")
	}
	jwt.TimeFunc = time.Now
	claims.StandardClaims.ExpiresAt = time.Now().Add(2 * time.Hour).Unix()
	return GenerateToken(claims)
}
